/** @file    BundleRequirements.cpp
 *  @author  Alessandro Polo
 *  @version $Id: BundleRequirements.cpp 2833 2010-08-02 23:12:57Z alex $
 *  @brief
 * File containing methods for the wosh::BundleRequirements class.
 * The header for this class can be found in BundleRequirements.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <core/BundleRequirements.h>

 #include <core/ObjectAllocator.h>
 #include <core/Functional.h>
 #include <core/DataModels.h>
 #include <core/List.h>
 #include <core/Table.h>


using namespace std;
namespace wosh {

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 WOSH_REGISTER(wosh::BundleRequirement, "wosh::ISerializable", 1.01, _static_BundleRequirement)

BundleRequirement& BundleRequirement::operator=(const BundleRequirement& m) {
	if (this == &m) return *this; // same object?
	this->reqScope = m.reqScope;
	this->classInfo = m.classInfo;
	this->locationHopsMax = m.locationHopsMax;
	return *this;
 }

bool BundleRequirement::operator==(const BundleRequirement &other) const {
	if ( this->classInfo != other.classInfo ) return false;
	if ( this->reqScope != other.reqScope ) return false;
	if ( this->locationHopsMax != other.locationHopsMax ) return false;
	return true;
 }

DataFieldCollector<BundleRequirement>* BundleRequirement::getDataFields() {
	DataFieldCollector<BundleRequirement>* fields = new DataFieldCollector<BundleRequirement>();
	fields->add<const std::string&, Variant>(&BundleRequirement::getClassType, "ClassType", 0);
	fields->add<double, Variant>(&BundleRequirement::getClassVersionMin, "ClassVersionMin", 1);
	fields->add<int, Variant>(&BundleRequirement::getLocationHopsMax, "LocationHopsMax", 2);
	fields->add<int,Variant>(&BundleRequirement::getScopes, "Scopes", 3);
	return fields;
 }

std::string BundleRequirement::toString() const {
	return Utilities::format("%s[%s,%g]", getClassNameOnlyOf(getClassName()), this->classInfo.name.c_str(), this->classInfo.version );
 }

bool BundleRequirement::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = true;
	ret = this->classInfo.updateHash( hash_context ) & ret;
	ret = hash_context->update( this->locationHopsMax ) & ret;
	ret = hash_context->update( this->reqScope ) & ret;
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 WOSH_REGISTER(wosh::BundleRequirements, "wosh::ISerializable", 1.01, _static_BundleRequirements)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

BundleRequirements::~BundleRequirements() {
	clear();
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT BundleRequirements::add( BundleRequirement* requirement ) {
	if ( requirement == NULL ) return WRET_ERR_PARAM;
	MutexLockerWrite mL(getMutex());
	ConstIterator it;
	const ConstIterator it_end = this->end();
	for ( it=this->begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		if ( *it == requirement ) return WRET_ERR_PARAM;
		if ( *(*it) == *requirement ) return WRET_ERR_PARAM;
	 }
	this->push_back(requirement);
	return WRET_OK;
 }

WRESULT BundleRequirements::add( const std::string& classType, Bundle::BUNDLE_STATE scope, double interfaceVersionMin ) {
	BundleRequirement* breq = new BundleRequirement();
	breq->setClassType( classType );
	breq->setClassVersionMin( interfaceVersionMin );
	breq->setScopes( scope );
	WRESULT ret = add(breq);
	if ( WFAILED(ret) )
		delete breq;
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

int BundleRequirements::count( Bundle::BUNDLE_STATE scope ) const {
	MutexLockerRead mL(getMutex());
	int found = 0;
	BundleRequirements::ConstIterator it;
	const BundleRequirements::ConstIterator it_end = this->end();
	for ( it=this->begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		if ( (*it)->getScopes() & (int)scope )
			++found;
	 }
	return found;
 }

int BundleRequirements::getRequirementsOf( vector<BundleRequirement*>& req_list, Bundle::BUNDLE_STATE scope ) const {
	BundleRequirements::ConstIterator it;
	const BundleRequirements::ConstIterator it_end = this->end();
	for ( it=this->begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		if ( (*it)->getScopes() & (int)scope )
			req_list.push_back( new BundleRequirement(*(*it)) );
	 }
	return req_list.size();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

BundleRequirements& BundleRequirements::operator=(const BundleRequirements& m){
	if (this == &m) return *this; // same object?

	return *this;
 }

bool BundleRequirements::operator==(const BundleRequirements &other) const {
	if ( this->size() != other.size() ) return false;

	return true;
 }
 
bool BundleRequirements::operator>(const BundleRequirements& other) const {
	if ( this->size() > other.size() ) return true;

	return false;
 }

bool BundleRequirements::operator<(const BundleRequirements& other) const {
	if ( this->size() < other.size() ) return true;

	return false;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::string BundleRequirements::toString() const {
	return Utilities::format("%s[%d]", getClassNameOnlyOf(getClassName()), this->size() );
 }

bool BundleRequirements::updateHash( DataHashContext* hash_context ) const {
	if ( hash_context == NULL ) return false;
	bool ret = true;
	BundleRequirements::ConstIterator it;
	const BundleRequirements::ConstIterator it_end = this->end();
	for ( it=this->begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		ret = (*it)->updateHash( hash_context ) & ret;
	 }
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

List* BundleRequirements::toList() const
 {
	this->transactionBeginRead();
	List* objs = new List(this->size());
	for_each( this->begin(), this->end(),
				Functor_Appender<List, BundleRequirement, const std::string&>(*objs, &BundleRequirement::getClassType ) );
	this->transactionEnd();
	return objs;
 }

Table* BundleRequirements::toTable() const
 {
	this->transactionBeginRead();
	Table* table = new Table( this->size(), 4, true );
	table->setTableName("Requirements");
	table->setHeaderHorizontal("Scopes", 0);
	table->setHeaderHorizontal("Type", 1);
	table->setHeaderHorizontal("Hops (Max)", 2);
	table->setHeaderHorizontal("Version (Min)", 3);
	unsigned int iRow = 0;
	ConstIterator it;
	const ConstIterator it_end = this->end();
	for ( it=this->begin(); it!=it_end; ++it, ++iRow ) {
		if ( *it == NULL ) continue;
		table->set<Variant>( (*it)->getScopes(), iRow, 0 );
		table->set<Variant>( (*it)->getClassType(), iRow, 1 );
		table->set<Variant>( (*it)->getClassVersionMin(), iRow, 2 );
		table->set<Variant>( (*it)->getLocationHopsMax(), iRow, 3 );
	 }
	this->transactionEnd();
	return table;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

}; // namespace wosh
